#include "stdafx.h"
#include "SmartPtrManager.h"
#include <algorithm>
#include <assert.h>



UINT CSmartPtrManager::GC_Generations::thresholdBytes[GENERATIONS_NUM] = {
	GENERATIONS_0_BYTES_LIM, 
	GENERATIONS_1_BYTES_LIM, 
	GENERATIONS_2_BYTES_LIM};

CSmartPtrManager* CSmartPtrManager::m_sSmartPtrManager = NULL;

CSmartPtrManager* CSmartPtrManager::GetInstance()
{
	if (m_sSmartPtrManager == NULL)
	{
		m_sSmartPtrManager = new CSmartPtrManager;
	}
	return m_sSmartPtrManager;
}

// if return true, meaning the generation 0 needs to be GC.
bool CSmartPtrManager::AddToManager( LinkNode* pSmartPtr, bool &bInsertSuccess )
{
	bool bNeedGC = false;
	bInsertSuccess = false;

	if (pSmartPtr == NULL)
	{
		return bNeedGC;
	}

	// this will always add the ptr to the generation 0

	// check whether the list has already contains the ptr
	list<LinkNode*>::iterator i = find( 
		gc_Generations[0].smartPtrList.begin(), 
		gc_Generations[0].smartPtrList.end(), 
		pSmartPtr);

	if ( i == gc_Generations[0].smartPtrList.end() )
	{
		// not in the list, then insert into the list

		// check whether the generation 0 is full
		if (!(HasEnoughSpaceForGeneration(0, pSmartPtr->GetSize() ) ) )
		{
			// not enough space in generation 0, so call a gc
			bNeedGC = true;
		}
		
		// insert anyway
		gc_Generations[0].smartPtrList.push_front(pSmartPtr);
		gc_Generations[0].currentBytes += pSmartPtr->GetSize();
		gc_Generations[0].objectCount++;
		bInsertSuccess = true;
		
	}

	return bNeedGC;
}

void CSmartPtrManager::RemveFromManager( LinkNode* pSmartPtr, bool &bRemoveSuccess )
{
	bRemoveSuccess = false;
	
	// the CSmartPtr itself, rather than its raw pointer, is NULL
	if (pSmartPtr == NULL)
	{
		// remove NULL is always false
		return;
	}

	for (int i = 0; i < GENERATIONS_NUM; i++)
	{
		list<LinkNode*>::iterator it = find( 
			gc_Generations[i].smartPtrList.begin(), 
			gc_Generations[i].smartPtrList.end(), 
			pSmartPtr);	
		
		if ( it != gc_Generations[i].smartPtrList.end() )
		{
			bRemoveSuccess = true;
			gc_Generations[i].smartPtrList.erase(it);
			gc_Generations[i].currentBytes -= pSmartPtr->GetSize();
			gc_Generations[i].objectCount--;
			break;
		}
	}
}

bool CSmartPtrManager::HasEnoughSpaceForGeneration( int generationNum, size_t size)
{
	bool bEnoughSpace = true;
	if (gc_Generations[generationNum].currentBytes + size > GC_Generations::thresholdBytes[generationNum])
	{
		bEnoughSpace = false;
	}
	return bEnoughSpace;
}

CSmartPtrManager::CSmartPtrManager()
{
	for (int i = 0; i < GENERATIONS_NUM; i++)
	{
		gc_Generations[i].currentBytes = 0;
		gc_Generations[i].objectCount = 0;
		gc_Generations[i].smartPtrList.clear();
	}
}

// just for test use, and should never be called outside UT
void CSmartPtrManager::Destroy()
{
	DELETE_IF_NOT_NULL(m_sSmartPtrManager);
}

CSmartPtrManager::~CSmartPtrManager()
{
	for (int i = 0; i < GENERATIONS_NUM; i++)
	{
		gc_Generations[i].smartPtrList.clear();
		gc_Generations[i].currentBytes = 0;
		gc_Generations[i].objectCount = 0;
	}	
}

// used by GC thread, which will collect the 0 to generationNum smart ptrs
// the side effect of this call is it will clear 0 to generationNum smartPtrList.
// if arg is 3, then merge generation 3, 2 and 1;
// if arg is 2, then merge generation 2 and 1;
void CSmartPtrManager::CollectNeedGcPtrs( int generationNum, list<LinkNode*>& listToReceive )
{
	assert(generationNum >= 0 && generationNum <= GENERATIONS_NUM);
	// clear the last merge result
	listToReceive.clear();

	// merge 
	for (int i = generationNum; i >= 0; i--)
	{
		// clear the gc_Generations[i].smartPtrList
		//listToReceive.merge(listToReceive.end(), gc_Generations[i].smartPtrList);
		
		// append copies of items in gc_Generations[i].smartPtrList
		listToReceive.insert(listToReceive.end(), gc_Generations[i].smartPtrList.begin(), gc_Generations[i].smartPtrList.end());
	}
}

// this function will be called by and after the GC, *remainPtrs do not need to deleted
int CSmartPtrManager::ShiftGcSurvivePtrs( int collectedGeneration, const list<LinkNode*>& remainPtrs)
{
	// no need GC at first
	int iNeedGC = -1;

	// clear the old generation, for later we will shift the whole generation to higher level
	for (int i = collectedGeneration; i >= 0; i--)
	{
		gc_Generations[i].smartPtrList.clear();

		gc_Generations[i].currentBytes = 0;
		gc_Generations[i].objectCount = 0;
	}

	// shift the not collected elements
	for (int i = (GENERATIONS_NUM - 1) - 1; /*we do not need to move the highest generation(GENERATIONS_NUM - 1)*/
		i > collectedGeneration; 
		i--)
	{
		// shift one level
		gc_Generations[i + 1].smartPtrList.splice(
			gc_Generations[i + 1].smartPtrList.end(), 
			gc_Generations[i].smartPtrList);
	}

	for (list<LinkNode*>::const_iterator it = remainPtrs.begin();
		it != remainPtrs.end();
		it++)
	{
		int originalGeneration = (*it)->GetOriginalGeneration();
		int newGeneration = 
			originalGeneration == GENERATIONS_NUM ? originalGeneration : originalGeneration + 1;

		// if the new generation is full after the insertion, just give a sign and gc later
		if (!HasEnoughSpaceForGeneration(newGeneration, (*it)->GetSize()))
		{
			iNeedGC = max(iNeedGC, newGeneration);
		}

		// do not need to worry the multi-add for the same object
		// for this method must be called after the CollectNeedGcPtrs, it will clear the original list
		// the other reason is one object will not appear twice in the heap
		gc_Generations[newGeneration].smartPtrList.push_front(*it);
		gc_Generations[newGeneration].currentBytes += (*it)->GetSize();
		gc_Generations[newGeneration].objectCount++;

		(*it)->SetOriginalGeneration(newGeneration);
	}

	return iNeedGC;
}

UINT CSmartPtrManager::GetGenerationObjectsCount( int generation )
{
	assert(generation >= 0 && generation <= GENERATIONS_NUM);
	return gc_Generations[generation].objectCount;	
}

ULONG CSmartPtrManager::GetGenerationObjectsSize( int generation )
{
	assert(generation >= 0 && generation <= GENERATIONS_NUM);
	return gc_Generations[generation].currentBytes;
}

bool CSmartPtrManager::IsEmbeddedPtr( LinkNode *ptr )
{
	// TODO: need lock?
	// Lock l;

	// in order to check whether the smart ptr is embedded,
	// we will have to check is it in the address scope of 
	// another object
	return CWrapperManager::GetInstance()->IsEmbeddedPtr(static_cast<void*>(ptr));
}

void CSmartPtrManager::SetPtrInUse( LinkNode* ptr, bool bInUse )
{
	CWrapperManager::GetInstance()->SetPtrInUse((void*)(*(static_cast<CSmartPtr<void>*>(ptr))), bInUse);
}

size_t CSmartPtrManager::GetSize( LinkNode* ptr )
{
	return CWrapperManager::GetInstance()->GetSize((void*)(*(static_cast<CSmartPtr<void>*>(ptr))));
}

bool CSmartPtrManager::IsPtrInUse( LinkNode* ptr )
{
	return CWrapperManager::GetInstance()->IsPtrInUse((void*)(*(static_cast<CSmartPtr<void>*>(ptr))));
}





